#light
namespace FSharp.WebTools.ClientControls

open System
open System.Web
open System.Web.UI
open System.Web.UI.WebControls

open Microsoft.FSharp.Quotations.Typed

open FSharp.WebTools
open FSharp.WebTools.Core
open FSharp.WebTools.ClientControls
open FSharp.WebTools.ClientLibrary

(*-----------------------------------------------------------------------------------------------*)

type IRepeaterItem = 
  inherit INamingContainer
  abstract RecdGet : string -> obj
  abstract RecdGetFormat : string * string -> obj
  abstract Prop : string -> obj
  abstract PropFormat : string * string -> obj
  abstract Value : obj with get
  
type RepeaterItem(o:obj) = 
  inherit System.Web.UI.Control()
  let getp s = 
    let m = (o.GetType().GetProperty(s)).GetGetMethod()
    m.Invoke(o, [| |]);
    
  let getpfm s (fm:string) = 
    let o = getp s
    let fmeth = o.GetType().GetMethods() |> Array.find (fun mi -> mi.Name = "ToString" && mi.GetParameters().Length = 1)
    fmeth.Invoke(o, [| box fm |])
    
  interface IRepeaterItem with
    member this.Value = o
    member this.Prop(s) = getp s
    member this.RecdGet(s) = getp s
    member this.PropFormat(s,fm) = getpfm s fm
    member this.RecdGetFormat(s,fm) = getpfm s fm

type MetaRepeaterItem() = 
  inherit System.Web.UI.Control()
  let generate s fm bget =
    let sget,scall = if bget then "get_","()" else "",""
    let s = 
      if (s = "" || s = null) then "" else 
        "."+sget+(s.Replace(".",scall+"."+sget))+scall
    let f = if (fm <> "") then ".ToString(''''"+fm+"'''')" else ""
    box (sprintf "''''+o%s%s+''''" s f)
    
  interface IRepeaterItem with
    member this.Value = (this :> IRepeaterItem).Prop("");
    member this.Prop(s) = generate s "" true
    member this.RecdGet(s) = generate s "" false
    member this.PropFormat(s,fm) = generate s (fm) true
    member this.RecdGetFormat(s,fm) = generate s (fm) false

[<MixedSide; ParseChildren(true)>]
type Repeater = class
  inherit ClientControl as base
  interface INamingContainer

  val mutable itemTempl : ITemplate 
  val mutable dataSource : Collections.IEnumerable 

  [<DuplexField>]
  val mutable clientItemTempl : string option
  [<WebBehavior(WebBehavior.ClientSide)>]
  val mutable clientData : obj option

  new() = { itemTempl = null; dataSource = null; clientItemTempl = None; clientData = None }

  [<PersistenceMode(PersistenceMode.InnerProperty); TemplateContainer(type IRepeaterItem)>]
  member this.ItemTemplate 
    with get() = this.itemTempl
    and set(v) = this.itemTempl <- v
    
  override this.OnLoad(e) = 
    this.clientItemTempl <-
      if (this.itemTempl <> null) then
        let c = new MetaRepeaterItem()
        this.itemTempl.InstantiateIn(c)
        c.DataBind();
        let sb = new Text.StringBuilder()
        use swr = new IO.StringWriter(sb)
        c.RenderControl(new HtmlTextWriter(swr));
        let jsc =
          [("\\", "\\\\"); ("\"", "\\\""); ("''''", "\""); ("\r\n", "\\n"); ("\n", "\\n")] 
          |> List.fold_left (fun (s:string) (w,r) -> s.Replace(w,r)) (sb.ToString())
        Some ("\"" + jsc + "\"")
      else
        None
    base.OnLoad(e)
    
  override this.Render(wr) =
    wr.Write("<div id=\"{0}\">", this.ClientID);
    if (this.dataSource <> null) then 
        this.dataSource |> (Seq.untyped_iter (fun (o:obj) ->
          if (this.itemTempl <> null) then
            let c = new RepeaterItem(o)
            this.itemTempl.InstantiateIn(c)
            c.DataBind();
            c.RenderControl(wr); )) 
    wr.Write("</div>");

  [<ReflectedDefinition>]    
  member this.DataSource 
    with get() : DuplexMonad<Collections.IEnumerable> = 
      makeDuplex 
        (server { return this.dataSource })
        (client { return failwith "databinding is todo" })
    and set(v:Collections.IEnumerable) = 
      makeDuplex
        (server { do  this.dataSource <- v; this.DataBind() })
        (client 
          { let vo = Internal.ClientCore.Cast(v)
            do! this.ClientDataBind(vo) })
  
  [<ReflectedDefinition>]    
  member this.ClientDataBind (v:obj) =
    client 
      { let tpl = match this.clientItemTempl with Some v -> v | _ -> "" 
        let ar  = Internal.ClientCore.Cast<obj[]>(v)
        let html = Array.fold_left (fun html o ->
              html + Internal.ClientCore.Eval(tpl)) "" ar
        do  this.InnerHtml <- html; }
      
  [<WebBehavior(WebBehavior.ClientConstructor); ReflectedDefinition>]
  member this.ClientCtor (el:IElement) =
    client 
      { do this.clientItemTempl <- None; }

  [<ReflectedDefinition>]
  member this.Client_Load ((sender:Object),(e:ClientLibrary.EventArgs)) =
    client { do (); }
    
end